import { Canvas, View } from "@tarojs/components"
import Taro, { CanvasContext, useReady } from "@tarojs/taro";
import { FC, forwardRef, Ref, useImperativeHandle, useRef, useState } from "react";

import "./index.module.less";

/**
 * 签名组件 ref context
 */
export interface CanvasSignContext {
  clear: () => void
  saveAsImage: () => Promise<ToDataURLResult>
}

/**
* CanvasSign.props 参数类型
*/
export interface CanvasSignProps {
  ref?: Ref<CanvasSignContext>
}

/**
 * canvas 导入图片结果
 */
export interface ToDataURLResult {
  tempFilePath: string
  errMsg: string
}

/**
 * 将 canvas 内容转换成 base64 字符串
 */
const toDataURL = async (canvasId: string, canvas?: CanvasContext): Promise<ToDataURLResult> => {
  if (!canvas) return { errMsg: 'canvas is null', tempFilePath: '' }

  return new Promise((resolve, reject) => {
    /** 
     * draw的回调函数是异步的，在调用canvasToTempFilePath方法时可能图片还没有生成完，
     * 所以要适当的加一些时间延迟。
     * 多次尝试ios手机可不加定时器，安卓手机基本上都要加，延迟的时间跟手机的性能有关，
     * 性能较弱的安卓机出现问题的概率比较大，延迟的时间试过200还是会偶现问题，设置为500基本有效。
     */
    canvas.draw(
      true,
      // @ts-ignore
      setTimeout(() => {
      Taro.canvasToTempFilePath({
        canvasId: canvasId,
        fileType: 'png',
        success: res => resolve(res),
        fail: err => reject(err)
      });
      }, 500)
    );
    });
};

/**
 * 获取 canvas 的尺寸
 */
const getCanvasSize = async (canvasId: string): Promise<{ height: number, width: number }> => {
  return new Promise((resolve) => {
    const query = Taro.createSelectorQuery()
    query.select('#' + canvasId)
      .boundingClientRect()
      .exec(([size]) => resolve(size))
  })
}


/**
 * 签名绘图 canvas 组件
 * 
 * @see https://juejin.cn/post/6978721559397531678
 */
export const CanvasSign: FC<CanvasSignProps> = forwardRef((props, ref) => {
  const [width, setWidth] = useState(0);
  const [height, setHeight] = useState(0);
  // 绘图画布引用
  const context = useRef<Taro.CanvasContext>();
  // 绘制轨迹信息
  const lineInfo = useRef({ startX: 0, startY: 0 });

  useReady(() => {
    const query = Taro.createSelectorQuery()

    query.select('#myCanvas')
      .context(res => {
        if (!res.context) return

        const { windowWidth, windowHeight } = Taro.getSystemInfoSync()
        setWidth(windowWidth)
        setHeight(windowHeight)

        res.context.setStrokeStyle('#000000');
        res.context.setLineWidth(4);
        res.context.setLineCap('round');
        res.context.setLineJoin('round');
        context.current = res.context as CanvasContext;
      })
      .exec()
  })

  const canvasStart = (e) => {
    e.preventDefault();
    lineInfo.current.startX = e.changedTouches[0].clientX
    lineInfo.current.startY = e.changedTouches[0].clientY
    context.current?.beginPath()
  }

  const canvasMove = (e) => {
    e.preventDefault();

    let x = e.changedTouches[0].clientX
    let y = e.changedTouches[0].clientY
    context.current?.moveTo(lineInfo.current.startX, lineInfo.current.startY)
    context.current?.lineTo(x, y)
    context.current?.stroke()
    context.current?.draw(true)
    lineInfo.current.startX = x
    lineInfo.current.startY = y
  }

  const clear = () => {
    context.current?.draw();
  }

  const saveAsImage = async () => {
    const { tempFilePath } = await toDataURL('myCanvas', context.current)
    console.log(tempFilePath,"tempFilePath")
    const { width, height } = await getCanvasSize('saveCanvas')

    // 这里完成了签名图片的旋转操作
    const saveCanvas = Taro.createCanvasContext('saveCanvas')
    saveCanvas.translate(0, height)
    saveCanvas.rotate(-90 * Math.PI / 180)
    saveCanvas.drawImage(tempFilePath, 0, 0, height, width)

    return await toDataURL('saveCanvas', saveCanvas)
  }

  useImperativeHandle(ref, () => ({ clear, saveAsImage }))

  return (
    <View >
      {/* 这个 canvas 用来签名 */}
      <Canvas
        className='signCanvas'
        canvasId='myCanvas'
        id='myCanvas'
        disableScroll
        onTouchStart={canvasStart}
        onTouchMove={canvasMove} style={{ width, height }} // 直接在 style 属性中设置

      ></Canvas>

      {/* 这个 canvas 用于把签名内容旋转九十度 */}
      <Canvas
        className='saveCanvas'
        // style={{ position: 'absolute', top: '100wv', left: '100vw', zIndex: -1 }}
        canvasId='saveCanvas'
        id='saveCanvas'
      ></Canvas>

    </View>
  )
})
